/**
 * blob
 */

import React from 'react';
import { Balloon, Button, Message, Result } from '@teamix/ui';
import { YunxiaoIcon, ButtonIcon } from '@alife/devops-ui';
import copy from 'copy-to-clipboard';
import utils from 'utils';
import FilePanel from 'components/filePanel';
import Highlighter from 'components/highlighter';
import MarkdownIt from 'markdown-it';
import MarkdownItReplaceLink from 'markdown-it-replace-link';
import hljs from 'utils/hl.js';
import {
  maxBlobSize,
  PROJECT_WRITE_ROLES,
  ENUM,
  maxImgBlobSize,
  PROJECT_ADMIN_ROLES,
} from 'utils/constants.js';
import OtherOption from './dialog/otherOption';
import DeleteFileBtnWithDialog from './dialog/deleteFile';
import LfsTag from 'components/lfs/lfsTag';
import AccessContainer from 'components/accessContainer';
import NotBranchDisabledButton from '../content/components/notBranchDisabledButton';
import { access, protectBranchCheck } from 'components/accessContainer/util';
// import { getWebIdeUrl } from 'api';
import { navigate, getPathWithRootPath, navigateOpen, HrefLink } from 'utils/router';
import { filterXSS } from 'xss';
import FileIcon from 'components/fileIcon';
import ListEmpty from 'atComponents/ListEmpty';
import track from 'utils/track';
import Ipynb from './ipynb';
import PanelSkeleton from './components/skeleton';
import { Perf } from '@alife/yunxiao-common';
import exceed from 'utils/apimap';

import intl from '../../../../../../../../locale';

const { Tooltip } = Balloon;
const { pathResource, resourceSubject } = window.FORCE;
const md = new MarkdownIt({
  html: true,
  // linkify: true,
  highlight(str, lang) {
    if (lang && hljs.getLanguage(lang)) {
      try {
        return `<pre><code class="hljs">${hljs.highlightAuto(str).value}</code></pre>`;
      } catch (_) {
        return '';
      }
    }
    return ''; // 使用额外的默认转义
  },
  replaceLink(link, env, token) {
    // 若 md 中链接不以 http 开头且非以 # 开头（这种是锚点），普通链接在前面加上 blobPathPrefix，图片链接加上 rawPathPrefix
    // 此逻辑与gitlab保持一致
    const blobPathPrefix = getPathWithRootPath(`/${pathResource.fullPath}/blob/${utils.revisionEncodeURI(
      resourceSubject.revision,
    )}/`);
    const rawPathPrefix = getPathWithRootPath(`/${pathResource.fullPath}/raw/${utils.revisionEncodeURI(
      resourceSubject.revision,
    )}/`);
    if (link.indexOf('http') !== 0 && link.indexOf('#') !== 0) {
      if (token.type === 'image') {
        return `${rawPathPrefix}${link}`;
      } else {
        return `${blobPathPrefix}${link}`;
      }
    }
    // 链接新开窗口
    token.attrPush(['target', '_blank']);
    return link;
  },
}).use(MarkdownItReplaceLink);

// 防止浏览器记录滚动条位置，为了滚到定位行
/* eslint-disable no-restricted-globals */
if ('scrollRestoration' in history) {
  history.scrollRestoration = 'manual';
} else {
  window.onunload = () => window.scrollTo(0, 0);
}
/* eslint-enable no-restricted-globals */

export default class BlobCompoent extends React.Component {
  state = {
    hasJumpedLineCode: false,
  };

  componentDidUpdate() {
    // jump_line_code 状态不为空，即认为有未消费的待跳转行号
    // 这时需判断有无这行的dom（因为不知啥时候diff组件能渲染完），有的话进行跳转，并清空 jump_line_code
    // 注意一个blob只允许定位一次，这是为了后面点行号不产生跳动
    if (this.props.jump_line_code && !this.state.hasJumpedLineCode) {
      const lineAnchorDom = document.getElementById('to-jump-line');

      if (lineAnchorDom) {
        const $container = document.querySelector('.project-file-layout-content');

        if (!$container) {
          return;
        }

        // 先滚到最上面
        $container.scrollTop = 0;
        // 91是一二级导航的高度，再留出container高度的四分之一
        $container.scrollTop =
          lineAnchorDom.getBoundingClientRect().top - 91 - $container.offsetHeight / 4;

        this.setState({
          hasJumpedLineCode: true,
        });

        this.props.updateJumpLineCode(null);
      }
    }
  }

  // goWebIde = (e, path) => {
  //   const { revisionType } = resourceSubject;
  //   e.preventDefault();
  //   track({
  //     action: 'goto_webide',
  //     page: 'Repo-Files',
  //   });
  //   getWebIdeUrl({
  //     filepath: path,
  //     branch: revisionType === 'branch' ? resourceSubject.revision : '',
  //     commit: revisionType === 'sha1' ? resourceSubject.revision : '',
  //     line: '',
  //     column: '',
  //     type: 'view',
  //     merge_commit: '',
  //     mergerequestId: '',
  //   })
  //     .then((result) => {
  //       navigateOpen(result.endpoint);
  //     })
  //     .catch((err) => {
  //       console.error(err);
  //     });
  // };

  goEdit = () => {
    const { resourcePath } = this.props;
    navigate(
      `/${pathResource.fullPath}/edit/${utils.revisionEncodeURI(
        resourceSubject.revision,
      )}/${encodeURIComponent(resourcePath)}`,
    );

    this.props.updateSetSelectedType({
      resourceSubject: {
        subject: 'edit',
        path: resourcePath || '',
      },
    });
  };

  onLineNumberClick = (e, line_code) => {
    // 已选中的行号
    const selectedLineCode = window.location.hash.match(/^#L([0-9]+)/)
      ? Number(window.location.hash.match(/^#L([0-9]+)/)[1])
      : null;
    if (e.shiftKey && selectedLineCode && selectedLineCode < line_code) {
      window.location.hash = `L${selectedLineCode}-${line_code}`;
    } else {
      window.location.hash = `L${line_code}`;
    }
    copy(window.location.href);
    Message.show({ type: 'success', content: '文件行号分享链接已复制到剪切板' });
    this.props.updateJumpLineCode(line_code);
  };

  goLfsSetting = () => {
    navigateOpen(`/${pathResource.fullPath}/settings/lfs#delete`, '_blank');
    track({
      action: 'view_lfs_file',
      page: '',
      type: '',
      control: 'tag',
    });
  };

  lfsDownLoadhander = async () => {
    const { lfs_oid } = this.props;
    const projectId = window.FORCE.pathResource.id;
    // 获取oss下载链接
    const ossHref = await exceed.fetch({
      api: 'project_blobs_lfs_download_href',
      params: { projectId, oid: lfs_oid },
    });
    if (ossHref && ossHref.download_link) {
      navigateOpen(ossHref.download_link);
    } else {
      Message.error({
        title: intl.get({
          id: 'code-assets.containers.content.blob.UnableToDownloadTheFile',
          defaultMessage: '文件下载失败',
        }),
        duration: 2000,
      });
    }
  };

  renderBlobContentJsx() {
    const { blob, renderType, blobSize, resourcePath, is_lfs, isFileNotFound, fileReqFailInfo } =
      this.props;
    const fileName = resourcePath.split('/')[resourcePath.split('/').length - 1];
    const { role } = pathResource;
    // 资源被删除时
    if (isFileNotFound) {
      return (
        <ListEmpty
          size="small"
          type="card"
          content={intl.get({
            id: 'code-assets.containers.content.blob.DocumentResourceDoesNotExist',
            defaultMessage: '文件资源不存在',
          })}
          description={
            access(PROJECT_ADMIN_ROLES, role) ? (
              <a className="no-href-with-click" onClick={this.goLfsSetting}>
                {intl.get({
                  id: 'code-assets.containers.content.blob.ViewLfsFileDeletionRecords',
                  defaultMessage: '查看 LFS 文件删除记录',
                })}
              </a>
            ) : (
              intl.get({
                id: 'code-assets.containers.content.blob.ToQueryTheLfsFile',
                defaultMessage: '如需查询 LFS 文件历史，请联系管理员处理',
              })
            )
          }
          image="none"
          className="no-content no-btns"
        />
      );
    }
    // 请求异常时，显示错误原因
    if (fileReqFailInfo) {
      return (
        <ListEmpty
          size="small"
          type="card"
          content={fileReqFailInfo}
          image="none"
          className="no-content no-btns"
        />
      );
    }

    // renderType 类型有 TEXT, BINARY 两种，BINARY需要判断后缀是图片时，展示图片，其余展示成下载
    let blobContentJsx;
    // 要跳到的是第一行
    const jump_line_code = window.location.hash.match(/^#L([0-9]+)/)
      ? Number(window.location.hash.match(/^#L([0-9]+)/)[1])
      : null;
    // selectedLines是个数组，可能是多行
    const selectedLines = jump_line_code
      ? window.location.hash
        .slice(2)
        .split('-')
        .map((item) => Number(item))
      : [];
    switch (renderType) {
      case 'TEXT': {
        // 如果文件大小大于阈值，则返回下载格式
        if (!is_lfs && blobSize < maxBlobSize) {
          // 内容为ipynb文件则进行进行解析
          if (utils.isIpynb(resourcePath) && utils.isJSON(blob)) {
            blobContentJsx = (
              <FilePanel.Body>
                <Ipynb blob={blob} />
              </FilePanel.Body>
            );
          } else if (!utils.isMarkdown(resourcePath)) {
            const language = `${fileName.split('.')[fileName.split('.').length - 1]}`;
            let highlightLanguage = '';
            switch (language) {
              case 'vue':
              case 'ux': // 快应用后缀，语法类似vue
                // FIXME 临时解决vue高亮问题，待后续替换高亮组件
                // https://aone.alibaba-inc.com/v2/project/940160/bug/39552297
                highlightLanguage = 'cpp';
                break;
              case 'html':
                highlightLanguage = 'html';
                break;
              default:
                highlightLanguage = '';
            }
            // 内容为md格式为，渲染md
            blobContentJsx = (
              <FilePanel.Body noPadding>
                {blob !== '' ? (
                  <div id="highlighter-blob-content">
                    <Highlighter
                      // language={`${fileName.split('.')[fileName.split('.').length - 1]}`}
                      // 缺陷https://work.aone.alibaba-inc.com/issue/30017264，先简单修复vue文件style识别内容丢失问题
                      // 缺陷：https://work.aone.alibaba-inc.com/issue/30620064， html内容丢失
                      language={highlightLanguage}
                      wrapLines
                      lineProps={(lineNumber) => {
                        const props = {};
                        if (jump_line_code === lineNumber) {
                          props.id = 'to-jump-line';
                        }
                        if (
                          selectedLines[0] <= lineNumber &&
                          selectedLines[selectedLines.length - 1] >= lineNumber
                        ) {
                          props.style = {
                            background: '#fffbdd',
                            display: 'block',
                          };
                        }
                        return props;
                      }}
                      selectedLines={selectedLines}
                      onLineNumberClick={this.onLineNumberClick}
                    >
                      {blob}
                    </Highlighter>
                  </div>
                ) : (
                  <div className="binary-placeholder">
                    {intl.get({
                      id: 'code-assets.src.utils.locale.EmptyFile',
                      defaultMessage: '空文件',
                    })}
                  </div>
                )}
              </FilePanel.Body>
            );
          } else {
            // md文件中对图片的引用路径，相对路径改绝对路径
            let blobStr = blob || '';
            const reg = /\[.*?\]\(.*?\)/g;
            const imgs = blobStr.match(reg);
            imgs &&
              imgs.forEach((v) => {
                const newPath = utils.transformRelativePathToAbsolutePath(v, resourcePath);
                blobStr = blobStr.replace(v, newPath);
              });
            blobContentJsx = (
              <FilePanel.Body>
                <div
                  className="markdown-body"
                  dangerouslySetInnerHTML={{
                    __html: filterXSS(md.render(blobStr || '')),
                  }}
                />
              </FilePanel.Body>
            );
          }
        } else {
          blobContentJsx = (
            <FilePanel.Body>
              <div style={{ textAlign: 'center' }}>
                <HrefLink
                  className={is_lfs ? '' : 'text-black'}
                  style={{ fontSize: '16px', display: 'block', margin: is_lfs ? 0 : '12px 0 12px' }}
                  href={`/${pathResource.fullPath}/raw/${utils.revisionEncodeURI(
                    resourceSubject.revision,
                  )}/${resourcePath}`}
                  target="_blank"
                  rel="noreferrer"
                >
                  {intl.get({
                    id: 'code-assets.src.utils.locale.ClickToViewTheOriginal',
                    defaultMessage: '点击查看原始文件',
                  })}
                </HrefLink>
                {!is_lfs && (
                  <div className="text-grey" style={{ fontSize: '14px' }}>
                    {intl.get({
                      id: 'code-assets.src.utils.locale.SorryTheCurrentFileIs',
                      defaultMessage: '抱歉，当前文件过大无法在线浏览',
                    })}
                  </div>
                )}
              </div>
            </FilePanel.Body>
          );
        }
        break;
      }
      case 'BINARY':
        if (utils.isImageSuffix(fileName) && blobSize < maxImgBlobSize) {
          blobContentJsx = (
            <FilePanel.Body>
              <div style={{ textAlign: 'center' }}>
                <img
                  src={getPathWithRootPath(`/${pathResource.fullPath}/raw/${utils.revisionEncodeURI(
                    resourceSubject.revision,
                  )}/${resourcePath}`)}
                  style={{ maxWidth: '100%' }}
                />
              </div>
            </FilePanel.Body>
          );
        } else {
          blobContentJsx = (
            <FilePanel.Body>
              <div className="tc not-previewable">
                <Result
                  img={
                    'https://img.alicdn.com/imgextra/i2/O1CN01hDnTJx1MfdkhIFqzY_!!6000000001462-2-tps-560-560.png'
                  }
                  desc={
                    utils.isImageSuffix(fileName)
                      ? intl.get({
                        id: 'code-assets.containers.content.blob.TheFileIsTooLarge',
                        defaultMessage: '文件过大，不支持预览，请下载至本地查看',
                      })
                      : intl.get({
                        id: 'code-assets.containers.content.blob.TheCurrentFileDoesNot',
                        defaultMessage: '当前文件不支持预览，请下载至本地查看',
                      })
                  }
                  theme={'yunxiao'}
                  extra={
                    !is_lfs ? (
                      <Button
                        type="primary"
                        component="a"
                        href={getPathWithRootPath(`/${pathResource.fullPath}/raw/${utils.revisionEncodeURI(
                          resourceSubject.revision,
                        )}/${resourcePath}?type=download`)}
                        target="_blank"
                      >
                        <span>
                          <YunxiaoIcon type="export-module-line" />
                          {intl.get({
                            id: 'code-assets.containers.content.blob.DownloadObjects',
                            defaultMessage: '下载文件',
                          })}
                        </span>
                      </Button>
                    ) : null
                  }
                />
                {is_lfs && (
                  <Button type="primary" onClick={this.lfsDownLoadhander}>
                    {' '}
                    <YunxiaoIcon type="export-module-line" />
                    {intl.get({
                      id: 'code-assets.containers.content.blob.DownloadObjects',
                      defaultMessage: '下载文件',
                    })}
                  </Button>
                )}
              </div>
            </FilePanel.Body>
          );
        }
        break;
      default:
        break;
    }

    return blobContentJsx;
  }

  disallowEditHintMessage = (data) => {
    // text={isTagRevision ? '只有分支下才可以操作' : (
    //   <React.Fragment>
    //     {!access(PROJECT_WRITE_ROLES, role) ? '无权限进行编辑文件操作，请联系管理员申请对应的权限' : (
    //       <React.Fragment>
    //         {isDeveloper ? '当前为保护分支，无权限直接推送，请联系管理员处理' : '当前为保护分支，无权限直接推送，可前往设置处理'}
    //       </React.Fragment>
    //     )}
    //   </React.Fragment>
    // )}
    if (data.isTagRevision) {
      return intl.get({
        id: 'code-assets.containers.content.blob.OperationsCanOnlyBePerformed',
        defaultMessage: '只有分支下才可以操作',
      });
    }

    if (data.isMirrorSynchronizing) {
      return intl.get({
        id: 'code-assets.containers.content.blob.TheRepositoryIsBeingSynchronized',
        defaultMessage: '仓库同步中，暂不支持操作，请稍后再试',
      });
    }

    if (!access(PROJECT_WRITE_ROLES, data.role)) {
      return intl.get({
        id: 'code-assets.containers.content.blob.YouAreNotAuthorizedTo',
        defaultMessage: '无权限进行编辑文件操作，请联系管理员申请对应的权限',
      });
    }

    if (data.isDeveloper) {
      return intl.get({
        id: 'code-assets.containers.content.blob.TheCurrentBranchIsProtected',
        defaultMessage: '当前为保护分支，无权限直接推送，请联系管理员处理',
      });
    }

    return intl.get({
      id: 'code-assets.containers.content.blob.CurrentlyItIsAProtected',
      defaultMessage: '当前为保护分支，无权限直接推送，可前往设置处理',
    });
  };

  disallowDeleteHintMessage = (data) => {
    // text={isTagRevision ? '只有分支下才可以操作' : (
    //   <React.Fragment>
    //     {!access(PROJECT_WRITE_ROLES, role) ? '无权限进行删除文件操作，请联系管理员申请对应的权限' : (
    //       <React.Fragment>
    //         {isDeveloper ? '当前为保护分支，无权限直接推送，请联系管理员处理' : '当前为保护分支，无权限直接推送，可前往设置处理'}
    //       </React.Fragment>
    //     )}
    //   </React.Fragment>
    // )}

    if (data.isTagRevision) {
      return intl.get({
        id: 'code-assets.containers.content.blob.OperationsCanOnlyBePerformed',
        defaultMessage: '只有分支下才可以操作',
      });
    }

    if (data.isMirrorSynchronizing) {
      return intl.get({
        id: 'code-assets.containers.content.blob.TheRepositoryIsBeingSynchronized',
        defaultMessage: '仓库同步中，暂不支持操作，请稍后再试',
      });
    }

    if (!access(PROJECT_WRITE_ROLES, data.role)) {
      return intl.get({
        id: 'code-assets.containers.content.blob.YouAreNotAuthorizedTo.1',
        defaultMessage: '无权限进行删除文件操作，请联系管理员申请对应的权限',
      });
    }

    if (data.isDeveloper) {
      return intl.get({
        id: 'code-assets.containers.content.blob.TheCurrentBranchIsProtected',
        defaultMessage: '当前为保护分支，无权限直接推送，请联系管理员处理',
      });
    }

    return intl.get({
      id: 'code-assets.containers.content.blob.CurrentlyItIsAProtected',
      defaultMessage: '当前为保护分支，无权限直接推送，可前往设置处理',
    });
  };

  render() {
    const {
      blob,
      blobSize: originalBlobSize,
      renderType,
      resourcePath,
      isGettingBlob,
      protectedBranch,
      is_lfs,
      isFileNotFound,
      fileReqFailInfo,
      lastMirrorSyncRecord,
    } = this.props;
    const { role, isArchive } = pathResource;
    const { revisionType } = resourceSubject;
    const fileName = resourcePath.split('/')[resourcePath.split('/').length - 1];

    if (isGettingBlob) {
      return <PanelSkeleton delay={1000} />;
    }

    // 注意，这里要严格等于 null ，因为可能文件内容为空，这时也要展示的, isFileNotFound 标识lfs文件被删除，显示占位图
    // lfs 被删除时和文件请求失败时显示错误原因
    const isBlobReqSuccess = !isFileNotFound && !fileReqFailInfo;
    if (blob === null && isBlobReqSuccess) {
      return null;
    }

    let blobSize = null;
    if (originalBlobSize) {
      if (originalBlobSize > 1024) {
        blobSize = Math.round(originalBlobSize / 1024);
        if (blobSize > 1024) {
          blobSize = `${Math.round(blobSize / 1024)} MB`;
        } else {
          blobSize = `${blobSize} KB`;
        }
      } else {
        blobSize = `${originalBlobSize} Bytes`;
      }
    }

    const { access_level } = ENUM;
    const allowProtectPush = protectBranchCheck(protectedBranch, role);
    const isDeveloper = role === access_level[30];
    const isTagRevision = revisionType === 'tag' || revisionType === 'sha1';

    let isMirrorSynchronizing = false;
    if (lastMirrorSyncRecord) {
      isMirrorSynchronizing = utils.isMirrorSynchronizing(lastMirrorSyncRecord.sync_status);
    }

    return (
      <React.Fragment>
        <FilePanel>
          <FilePanel.Header>
            <div className="file-box-header-detail">
              <div>
                <FileIcon className="header-file-icon" fileName={fileName} />
                <span className="display-inline-block">{fileName}</span>
                {is_lfs && <LfsTag role={role} />}
              </div>
              <div className="file-box-header-detail-right">
                {isArchive === 'false' && renderType === 'TEXT' && isBlobReqSuccess ? (
                  <React.Fragment>
                    {access(PROJECT_WRITE_ROLES, role) &&
                      allowProtectPush &&
                      !isMirrorSynchronizing ? (
                        <React.Fragment>
                          {revisionType !== 'branch' ? (
                            <NotBranchDisabledButton
                              tooltipText={intl.get({
                                id: 'code-assets.containers.content.blob.OnlyFilesUnderBranchesCan',
                                defaultMessage: '只有分支下的文件可以被编辑',
                              })}
                              isIcon
                              name="edit-line"
                              className="file-icon-margin-left"
                            />
                          ) : (
                            <Tooltip
                              trigger={
                                <ButtonIcon
                                  name="edit-line"
                                  onClick={this.goEdit}
                                  className="file-icon-margin-left"
                                />
                              }
                              text={intl.get({
                                id: 'code-assets.containers.content.blob.Editing',
                                defaultMessage: '编辑',
                              })}
                              align="t"
                            />
                          )}
                        </React.Fragment>
                    ) : (
                      <Tooltip
                        trigger={
                          <span><ButtonIcon name="edit-line" disabled className="file-icon-margin-left" /></span>
                        }
                        align="t"
                        text={this.disallowEditHintMessage({
                          isTagRevision,
                          role,
                          isDeveloper,
                          isMirrorSynchronizing,
                        })}
                      />
                    )}
                  </React.Fragment>
                ) : null}
                {isArchive === 'false' &&
                  allowProtectPush &&
                  access(PROJECT_WRITE_ROLES, role) &&
                  !isMirrorSynchronizing && (
                    <AccessContainer neededRole={PROJECT_WRITE_ROLES} role={role}>
                      <DeleteFileBtnWithDialog {...this.props} />
                    </AccessContainer>
                  )}

                {isArchive === 'false' &&
                  (!allowProtectPush ||
                    !access(PROJECT_WRITE_ROLES, role) ||
                    isMirrorSynchronizing) && (
                    <Tooltip
                      trigger={
                        <ButtonIcon name="delete-line" disabled className="file-icon-margin-left" />
                      }
                      align="tr"
                      text={this.disallowDeleteHintMessage({
                        isTagRevision,
                        role,
                        isDeveloper,
                        isMirrorSynchronizing,
                      })}
                    />
                  )}
                <OtherOption {...this.props} />
              </div>
            </div>
          </FilePanel.Header>
          <div>{this.renderBlobContentJsx()}</div>
          <Perf />
        </FilePanel>
      </React.Fragment>
    );
  }
}
